package com.xqboss.apps.controller.system;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.text.StrSplitter;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xqboss.apps.domain.param.AesParams;
import com.xqboss.apps.domain.user.SysUserWallet;
import com.xqboss.apps.dto.consumer.PasswordDto;
import com.xqboss.apps.dto.sys.SysUserDto;
import com.xqboss.apps.dto.user.UserApproveDto;
import com.xqboss.apps.service.consumer.ConsumerInviteRecordService;
import com.xqboss.apps.service.consumer.ConsumerRealNameService;
import com.xqboss.apps.service.consumer.ConsumerTeamService;
import com.xqboss.apps.service.guild.GuildMemberService;
import com.xqboss.apps.service.order.OrderQuickCopyRecordService;
import com.xqboss.apps.service.user.*;
import com.xqboss.apps.vo.sys.SysUserVo;
import com.xqboss.apps.vo.sys.UserApproveVo;
import com.xqboss.apps.vo.user.SysUserInfoVo;
import com.xqboss.chain.domain.chain.SysUserChain;
import com.xqboss.chain.service.SysUserChainService;
import com.xqboss.common.annotation.Log;
import com.xqboss.common.annotation.RepeatSubmit;
import com.xqboss.common.constant.Constants;
import com.xqboss.common.core.controller.BaseController;
import com.xqboss.common.core.domain.AjaxResult;
import com.xqboss.common.core.domain.R;
import com.xqboss.common.core.domain.entity.SysDictData;
import com.xqboss.common.core.domain.entity.SysUser;
import com.xqboss.common.core.domain.model.LoginBody;
import com.xqboss.common.core.domain.model.LoginUser;
import com.xqboss.common.core.domain.model.RegisterBody;
import com.xqboss.common.enums.BusinessType;
import com.xqboss.common.exception.ServiceException;
import com.xqboss.common.utils.SecurityUtils;
import com.xqboss.common.utils.StringUtils;
import com.xqboss.common.utils.aes.AesUtil;
import com.xqboss.common.utils.sms.SmsUtil;
import com.xqboss.framework.web.service.SysLoginService;
import com.xqboss.framework.web.service.SysRegisterService;
import com.xqboss.framework.web.service.TokenService;
import com.xqboss.system.mapper.SysDictDataMapper;
import com.xqboss.system.mapper.SysUserMapper;
import com.xqboss.system.service.ISysUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.StopWatch;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import static com.xqboss.common.utils.AsyncHandlerUtil.run;


/**
 * 登录验证
 *
 * @author Administrator
 */
@Api(tags = {"登录管理"})
@Slf4j
@RestController
@RequestMapping("/apps")
public class LoginController extends BaseController {
    @Autowired
    private SysLoginService loginService;
    @Autowired
    private ISysUserService userService;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysUserWalletService sysUserWalletService;
    @Autowired
    private ConsumerRealNameService consumerRealNameService;
    @Autowired
    private ConsumerTeamService consumerTeamService;
    @Resource
    private SysRegisterService sysRegisterService;
    @Resource
    private SysDictDataMapper sysDictDataMapper;
    @Autowired
    private SysUserChainService sysUserChainService;
    @Autowired
    private UserIdentityRankService userIdentityRankService;
    @Autowired
    private UserAssetWrenchService userAssetWrenchService;
    @Autowired
    private UserAssetLidouService userAssetLidouService;
    @Autowired
    private ConsumerInviteRecordService consumerInviteRecordService;
    @Autowired
    private UserIdentityPermIntegralService userIdentityPermIntegralService;
    @Autowired
    private OrderQuickCopyRecordService orderQuickCopyRecordService;
    @Autowired
    private YeepayWalletService yeepayWalletService;
    @Autowired
    private GuildMemberService guildMemberService;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private UserIdentityPermWrenchProductionService userIdentityPermWrenchProductionService;
    @Autowired
    private UserIdentityPermWrenchPresenterService  userIdentityPermWrenchPresenterService;
    @Autowired
    private UserIdentityPermLidouService userIdentityPermLidouService;


    @ApiOperation("用户名密码登录")
    @PostMapping("/login")
    public AjaxResult appsLogin(@RequestBody AesParams params) {
        AjaxResult ajax = AjaxResult.success();
        LoginBody loginBody = AesUtil.decryptToObject(params.getParamStr(), LoginBody.class);
        if(StringUtils.isBlank(loginBody.getUsername()) || StringUtils.isBlank(loginBody.getPassword())){
            throw new ServiceException("手机号/密码不能为空");
        }
        if (!ReUtil.isMatch(Constants.REGEX, loginBody.getPassword())) {
            throw new ServiceException("请输入6~10位数字字母密码");
        }
        // 生成令牌
        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), null,
                loginBody.getUuid(), true);
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }

    @ApiOperation("验证码登录")
    @PostMapping("/captchaLogin")
    public AjaxResult captchaLogin(@RequestBody LoginBody loginBody) {
        AjaxResult ajax = AjaxResult.success();
//        LoginBody loginBody = AesUtil.decryptToObject(params.getParamStr(), LoginBody.class);
        if (StrUtil.isBlank(loginBody.getUsername()) || StrUtil.isBlank(loginBody.getCode())) {
            return error("手机号/验证码不能为空");
        }
        // 生成令牌
        String token = loginService.login(loginBody.getUsername(), null, loginBody.getCode(),
                loginBody.getUuid(), true);
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }

    @ApiOperation("获取用户信息")
    @GetMapping("getInfo")
    public R<SysUserInfoVo> getInfo(@RequestParam(name = "isAsync",required = false,defaultValue = "true") Boolean isAsync) {

        //统计耗时
        StopWatch sw = new StopWatch();
        sw.start();

        //开始业务逻辑 ======》》》》》》》

        SysUserInfoVo vo = new SysUserInfoVo();

        //1)-用户信息
        SysUser user = sysUserMapper.selectUserById(SecurityUtils.getLoginUser().getUser().getUserId());

        vo.setSysUser(BeanUtil.copyProperties(user, SysUserVo.class));
        // 是否设置支付密码
        vo.getSysUser().setIsPayPwd(!StrUtil.isEmpty(user.getPayPwd()));
        // 是否设置密码
        vo.getSysUser().setIsPassword(!StrUtil.isEmpty(user.getPassword()));

        // --- 下方数据库查询操作改为异步优化(true-异步 false-同步)-------------------------------------------------------------------------------
        if (isAsync) {
            getUserInfoAsync(vo, user);
        } else {
            getUserInfo(vo, user);
        }

        //结束业务逻辑 《《《《《《《======
        sw.stop();
        log.info("[===获取用户信息总共耗时：{}===]",sw.getTotalTimeMillis());

        return R.ok(vo);
    }

    /**
     * 同步执行
     *
     * @param vo
     * @param user
     */
    private void getUserInfoAsync(SysUserInfoVo vo, SysUser user) {
        Long userId = user.getUserId();
        //1)-用户信息
        // 区块链地址
        CompletableFuture<Void> a1 = run(()->vo.getSysUser().setChainAddress(judgeChainAddress(user.getChainAddress(), userId)));
        // 兴趣爱好
        CompletableFuture<Void> a2 = run(()->vo.getSysUser().setHobbies(judgeHobbies(user.getHobbies())));

        //钱包余额
        CompletableFuture<Void> a3 = run(()->vo.getSysUser().setUserWallet(sysUserWalletService.lambdaQuery().eq(SysUserWallet::getUserId, userId).list()));
        // 扳手钱包余额
        CompletableFuture<Void> a4 = run(()->vo.getSysUser().setUserAssetWrench(userAssetWrenchService.getByUserId(userId)));
        // 力豆钱包余额
        CompletableFuture<Void> a5 = run(()->vo.getSysUser().setUserAssetLidou(userAssetLidouService.getLidouInfo(userId)));
        // 是否开通易宝钱包
        CompletableFuture<Void> a6 = run(()->vo.getSysUser().setIsCreateYeeWallet(yeepayWalletService.getUserWallet(userId) != null));
        // 好友邀请数
        CompletableFuture<Void> a7 = run(()->vo.getSysUser().setInviteNum(consumerInviteRecordService.getInviteNum(userId)));
        // 返回用户是否具备热豆转增权限 true具备
        CompletableFuture<Void> a8 = run(()->vo.getSysUser().setIsHotBeansGivePer(userIdentityPermIntegralService.getIsOpen(userId)));
        // 返回用户是否具备扳手转增权限 true具备
        CompletableFuture<Void> a9 = run(()->vo.getSysUser().setIsWrenchOpen(userIdentityPermWrenchPresenterService.getIsOpen(userId)));
        // 返回用户是否具备力豆转增权限 true具备
        CompletableFuture<Void> a10 = run(()->vo.getSysUser().setIsLidouOpen(userIdentityPermLidouService.getIsOpen(userId)));
        // 返回用户是否在快速抄货白名单里 true在
        CompletableFuture<Void> a11 = run(()->vo.getSysUser().setIsQuickCopyList(orderQuickCopyRecordService.getIsWhiteList(userId)));
        // 用户是否已经加入对应的公会（true-是）
        CompletableFuture<Void> a12 = run(()->vo.getSysUser().setIsJoinGuild(guildMemberService.getIsJoinGuild(userId)));
        // 返回用户是否具备开启扳手生产工厂权限
        CompletableFuture<Void> a13 = run(()->vo.getSysUser().setIsOpenWrenchProduction(userIdentityPermWrenchProductionService.getIsOpen(userId)));

        //2)-用户实名信息
        CompletableFuture<Void> b = run(()->vo.setRealName(consumerRealNameService.getRealName(userId)));

        //3)-用户队伍信息
        CompletableFuture<Void> c = run(()->vo.setTeam(consumerTeamService.getMyTeam(userId)));

        //4)-用户身份晋升信息
        CompletableFuture<Void> d = run(()->vo.setIdentityPromotionVo(this.userIdentityRankService.identityPromotion(userId)));

        //等待所有异步任务执行完返回
        CompletableFuture.allOf(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,b,c,d).join();
    }

    /**
     * 同步执行
     *
     * @param vo
     * @param user
     */
    private void getUserInfo(SysUserInfoVo vo, SysUser user) {
        Long userId = user.getUserId();
        //1)-用户信息

        // 区块链地址
        vo.getSysUser().setChainAddress(judgeChainAddress(user.getChainAddress(),userId));
        // 兴趣爱好
        vo.getSysUser().setHobbies(judgeHobbies(user.getHobbies()));
        // 钱包余额
        vo.getSysUser().setUserWallet(sysUserWalletService.lambdaQuery().eq(SysUserWallet::getUserId, userId).list());
        // 扳手钱包余额
        vo.getSysUser().setUserAssetWrench(userAssetWrenchService.getByUserId(userId));
        // 力豆钱包余额
        vo.getSysUser().setUserAssetLidou(userAssetLidouService.getLidouInfo(userId));
        // 是否开通易宝钱包
        vo.getSysUser().setIsCreateYeeWallet(yeepayWalletService.getUserWallet(userId) != null);
        // 好友邀请数
        vo.getSysUser().setInviteNum(consumerInviteRecordService.getInviteNum(userId));
        // 返回用户是否具备热豆转增权限 true具备
        vo.getSysUser().setIsHotBeansGivePer(userIdentityPermIntegralService.getIsOpen(userId));
        // 返回用户是否具备扳手转增权限 true具备
        vo.getSysUser().setIsWrenchOpen(userIdentityPermWrenchPresenterService.getIsOpen(userId));
        // 返回用户是否具备力豆转增权限 true具备
        vo.getSysUser().setIsLidouOpen(userIdentityPermLidouService.getIsOpen(userId));
        // 返回用户是否在快速抄货白名单里 true在
        vo.getSysUser().setIsQuickCopyList(orderQuickCopyRecordService.getIsWhiteList(userId));
        // 用户是否已经加入对应的公会（true-是）
        vo.getSysUser().setIsJoinGuild(guildMemberService.getIsJoinGuild(userId));
        // 返回用户是否具备开启扳手生产工厂权限
        vo.getSysUser().setIsOpenWrenchProduction(userIdentityPermWrenchProductionService.getIsOpen(userId));
        //2)-用户实名信息
        vo.setRealName(consumerRealNameService.getRealName(userId));

        //3)-用户队伍信息
        vo.setTeam(consumerTeamService.getMyTeam(userId));

        //4)-用户身份晋升信息
        vo.setIdentityPromotionVo(this.userIdentityRankService.identityPromotion(userId));
    }

    /**
     * 校验兴趣爱好
     *
     * @param hobbies
     * @return
     */
    private String judgeHobbies(String hobbies) {
        if (StrUtil.isNotBlank(hobbies)) {
            List<String> hobbie = StrSplitter.split(hobbies, ",", 0, true, true);
            List<SysDictData> typeList = sysDictDataMapper.selectList(new LambdaQueryWrapper<SysDictData>().in(SysDictData::getDictCode, hobbie));
            return typeList.stream().map(SysDictData::getDictLabel).collect(Collectors.joining(","));
        }
        return null;
    }

    /**
     * 判断用户区块链地址是否存在，不存在则更新
     *
     * @param chainAddress
     * @param userId
     */
    private String judgeChainAddress(String chainAddress, Long userId) {
        if(Objects.isNull(chainAddress)){
            SysUserChain chainInfo = sysUserChainService.getOrInitUserChain(userId);
            sysUserMapper.updateUserChainAddress(userId, chainInfo.getAddress());
            return chainInfo.getAddress();
        }
        return chainAddress;
    }

    @RepeatSubmit(interval = 1000, justUrl = true)
    @ApiOperation(value = "app修改登录密码")
    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
    @PutMapping("/updatePwd")
    public AjaxResult updatePwd(@RequestBody AesParams params) {
        RegisterBody param = AesUtil.decryptToObject(params.getParamStr(), RegisterBody.class);
        String oldPassword = param.getOldPwd();
        String newPassword = param.getNewPwd();
        LoginUser loginUser = getLoginUser();
        String userName = loginUser.getUsername();
        String password = loginUser.getPassword();
        boolean isMatch = ReUtil.isMatch(Constants.REGEX, newPassword);
        if (!isMatch) {
            return error("请输入6~10位数字字母密码");
        }
        if (!SecurityUtils.matchesPassword(oldPassword, password)) {
            return error("修改密码失败，旧密码错误");
        }
        if (SecurityUtils.matchesPassword(newPassword, password)) {
            return error("新密码不能与旧密码相同");
        }
        if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0) {
            // 更新缓存用户密码
            loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
            tokenService.setLoginUser(loginUser);
            return success();
        }
        return error("修改密码异常，请联系管理员");
    }

    @RepeatSubmit(interval = 1000, justUrl = true)
    @ApiOperation(value = "修改用户")
    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
    @PutMapping("/updateUser")
    public AjaxResult updateProfile(@Validated @RequestBody SysUserDto userDto) {
        LoginUser loginUser = getLoginUser();
        SysUser sysUser = loginUser.getUser();
        SysUser user = BeanUtil.copyProperties(userDto, SysUser.class, "userType");
        user.setUserName(sysUser.getUserName());
//        if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
//            return error("修改用户'" + user.getUserName() + "'失败，手机号码已存在");
//        }
//        if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
//            return error("修改用户'" + user.getUserName() + "'失败，邮箱账号已存在");
//        }
        user.setUserId(sysUser.getUserId());
        user.setPassword(null);
//        user.setAvatar(null);
        boolean b = userService.checkNickNameUnique(user.getUserId(), user.getNickName());
        if (!b) {
            return error("昵称重复，换个试试~");
        }
        if (userService.updateUserProfile(user) > 0) {
            // 更新缓存用户信息
            sysUser.setNickName(user.getNickName());
            sysUser.setSex(user.getSex());
            sysUser.setAvatar(user.getAvatar());
            sysUser.setHobbies(user.getHobbies());
            tokenService.setLoginUser(loginUser);
            return success();
        }
        return error("修改个人信息异常，请联系管理员");
    }

    @RepeatSubmit(interval = 1000, justUrl = true)
    @Log(title = "修改支付密码", businessType = BusinessType.UPDATE)
    @ApiOperation(value = "修改支付密码")
    @PostMapping(value = "/setPayPwd")
    public AjaxResult setPayPwd(@RequestBody AesParams param) {
        PasswordDto params = AesUtil.decryptToObject(param.getParamStr(), PasswordDto.class);
        log.info("修改支付密码：{}", params);
        SysUser loginUser = userService.selectUserById(getUserId());
        String userName = loginUser.getUserName();
        String password = loginUser.getPayPwd();
        if (!SecurityUtils.matchesPassword(params.getOldPwd(), password)) {
            return error("修改密码失败，旧密码错误");
        }
        if (!ReUtil.isMatch("^\\d+$", params.getNewPwd())) {
            return error("请输入6位数字密码");
        }
        if (SecurityUtils.matchesPassword(params.getNewPwd(), password)) {
            return error("新密码不能与旧密码相同");
        }
        if (userService.resetUserPayPwd(userName, SecurityUtils.encryptPassword(params.getNewPwd())) > 0) {
            return success();
        }
        return error("修改密码异常，请联系管理员");
    }

    @RepeatSubmit(interval = 1000, justUrl = true)
    @Log(title = "身份认证修改支付密码", businessType = BusinessType.UPDATE)
    @ApiOperation(value = "身份认证修改支付密码")
    @PostMapping(value = "/authenticationSetPayPwd")
    public AjaxResult authenticationSetPayPwd(@RequestBody AesParams param) {
        PasswordDto params = AesUtil.decryptToObject(param.getParamStr(), PasswordDto.class);
        if (ObjUtil.isNull(params) || StrUtil.isEmpty(params.getNewPwd())) {
            return error("参数错误");
        }
        SysUser loginUser = userService.selectUserById(getUserId());
        String userName = loginUser.getUserName();
        String password = loginUser.getPayPwd();
        if (SecurityUtils.matchesPassword(params.getNewPwd(), password)) {
            return error("新密码不能与旧密码相同");
        }
        if (!ReUtil.isMatch("^\\d+$", params.getNewPwd())) {
            return error("请输入6位数字密码");
        }
        SmsUtil.validateCaptcha(null, params.getCode(), null);
        if (userService.resetUserPayPwd(userName, SecurityUtils.encryptPassword(params.getNewPwd())) > 0) {
            return success();
        }
        return error("修改密码异常，请联系管理员");
    }

    @RepeatSubmit(interval = 1000, justUrl = true)
    @Log(title = "设置支付密码", businessType = BusinessType.UPDATE)
    @ApiOperation(value = "设置支付密码")
    @PostMapping(value = "/addPayPwd")
    public AjaxResult addPayPwd(@RequestBody AesParams param) {
        PasswordDto params = AesUtil.decryptToObject(param.getParamStr(), PasswordDto.class);
        String msg = sysRegisterService.updatePayPwd(params.getUserName(), params.getNewPwd(), params.getCode(), params.getBusinessType());
        return StringUtils.isEmpty(msg) ? success() : error(msg);
    }



    @ApiOperation(value = "授权用户信息")
    @GetMapping(value = "/approve")
    public R<UserApproveVo> approve(@Validated UserApproveDto dto) {
        String code = userService.buildApproveCode(getUserId());
        UserApproveVo vo = new UserApproveVo();
        vo.setCode(code);
        return R.ok(vo);
    }
}
